package com.develop.data.loan.jpa.attribute.converter;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.MethodCall;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

import java.lang.reflect.Modifier;

import static net.bytebuddy.description.annotation.AnnotationDescription.Builder.ofType;
import static net.bytebuddy.description.type.TypeDescription.Generic.Builder.parameterizedType;
import static net.bytebuddy.implementation.FieldAccessor.ofField;
import static net.bytebuddy.implementation.MethodDelegation.to;
import static net.bytebuddy.matcher.ElementMatchers.isDefaultConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;

public class CodedEnumAttributeConverterGenerator {
    //public static final String SUFFIX = "$AttributeConverterGeneratedByByteBuddy";
    public static final String SUFFIX = "AttributeConverter";

    public <CodedEnum> Class<?> generate(Class<CodedEnum> enumerable) {
        Class<?> clazz = null;
        try {
            clazz = new ByteBuddy()
                    .with(new NamingStrategy.AbstractBase() {
                        @Override
                        protected String name(TypeDescription superClass) {
                            return enumerable.getName() + SUFFIX;
                        }
                    })
                    .subclass(
                            parameterizedType(AttributeConverter.class, enumerable, String.class).build()
                    )
                    .annotateType(
                            ofType(Converter.class).define("autoApply", true).build()
                    )
                    .constructor(isDefaultConstructor())
                    .intercept(
                            MethodCall
                                    .invoke(Object.class.getDeclaredConstructor())
                                    .andThen(ofField("targetType").setsValue(enumerable))
                    )
                    .defineField("targetType", Class.class, Modifier.PRIVATE | Modifier.FINAL)
                    .method(named("convertToDatabaseColumn"))
                    .intercept(to(CodedEnumAttributeConverterInterceptor.class))
                    .method(named("convertToEntityAttribute"))
                    .intercept(to(CodedEnumAttributeConverterInterceptor.class))
                    .make()
                    .load(getClass().getClassLoader())
                    .getLoaded();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        return clazz;
    }
}
